Skip to content

Conversation

@opencode-agent
Copy link
Contributor

@opencode-agent opencode-agent bot commented Jan 6, 2026

All changes are in place. Let me create a summary of the implementation:

Summary

I've implemented the default variant configuration per agent feature as specified in the issue. Here are the changes made:

Files Modified

  1. packages/opencode/src/config/config.ts (lines 481, 497):

    • Added variant field to Config.Agent schema with description
    • Added variant to knownKeys set for proper parsing
  2. packages/opencode/src/agent/agent.ts (lines 37, 194):

    • Added variant field to Agent.Info type
    • Added parsing logic to read variant from config during agent state initialization
  3. packages/opencode/src/session/llm.ts (lines 86-87):

    • Updated variant resolution to use input.user.variant ?? input.agent.variant as the variant key
    • This implements the priority order: explicit user selection > agent's configured default > none
  4. packages/opencode/src/tool/task.ts (line 135):

    • Pass agent.variant to subagent sessions so they use their own configured variant

Usage

Users can now configure a default variant for each agent:

{
  "agent": {
    "build": {
      "model": "anthropic/claude-sonnet-4-5-thinking",
      "variant": "high"
    },
    "explore": {
      "model": "google/gemini-3-flash",
      "variant": "low"
    }
  }
}

Variant Priority Order

  1. Explicit user selection (--variant flag or ctrl+t)
  2. Agent's configured default variant (agent.variant)
  3. None (model's base behavior)

Subagent Behavior

Subagents now use their own configured agent.variant rather than inheriting the parent's --variant override, respecting explicit per-agent configuration.

Closes #7138

New%20session%20-%202026-01-06T23%3A49%3A15.558Z
opencode session  |  github run

@rekram1-node
Copy link
Collaborator

/oc I think this requires changes in tui too?

@opencode-agent
Copy link
Contributor Author

opencode-agent bot commented Jan 6, 2026

Now I have a full picture. Here's my analysis:

Does this PR require TUI changes?

Short answer: No, the TUI doesn't need changes for the basic functionality to work, but there could be a UX enhancement.

Here's why:

  1. Backend side works correctly: The llm.ts already uses input.user.variant ?? input.agent.variant, so if no user variant is selected, the agent's configured default variant will be used automatically.

  2. Current TUI behavior:

    • local.model.variant.current() returns the user-selected variant (stored per model in model.json)
    • When displaying the variant in the prompt UI, it only shows if local.model.variant.current() returns a value
    • If the user hasn't selected a variant, it shows nothing (even if the agent has a default configured)
  3. Potential UX enhancement (optional): The TUI could optionally show the agent's default variant in the UI when no user variant is selected. However, this is purely cosmetic since:

    • The backend already applies the agent's default variant correctly
    • The current behavior (showing nothing when user hasn't explicitly selected) is arguably correct since it represents what the user has chosen, not what will be applied
  4. What the PR already handles correctly:

    • Subagents use their own configured agent.variant (in task.ts)
    • The user.variant ?? agent.variant priority order works correctly

Conclusion: The TUI doesn't strictly require changes. The agent's default variant will be used correctly by the backend even without TUI changes. However, if you wanted to show the agent's default variant in the UI (as a visual indicator), that would be an optional UX enhancement, not a requirement.

New%20session%20-%202026-01-06T23%3A55%3A32.313Z
opencode session  |  github run

@CasualDeveloper
Copy link
Contributor

Good implementation! I noticed one potential gap:

Missing prompt.ts Change

The PR modifies llm.ts to use input.agent.variant as fallback, but prompt.ts also needs to be updated to store the resolved variant on the user message info.

In packages/opencode/src/session/prompt.ts around line 808, the current code is:

variant: input.variant,

This should be:

variant: input.variant ?? agent.variant,

Without this change, when input.variant is undefined (no explicit user selection), the agent's default variant won't be stored on the message info. The llm.ts change handles the LLM call correctly, but the message metadata won't reflect the actual variant used.


Also, consider adding a clarifying comment in task.ts explaining the design choice for subagent variant handling:

// Use subagent's configured variant (respects explicit per-agent config).
// Parent's --variant override does NOT cascade to subagents intentionally:
// if user configured oracle with variant: "high", that intent should be honored
// even when parent session uses --variant low.
variant: agent.variant,

This documents why subagents don't inherit the parent's variant selection.

@CasualDeveloper
Copy link
Contributor

CasualDeveloper commented Jan 7, 2026

I've also opened PR #7156 with an alternative implementation that includes:

  • The prompt.ts change (stores resolved variant in message metadata)
  • TUI support (same as this PR)
  • Desktop/web app support (additional)

Both PRs are viable - this one is simpler (9 files), #7156 is more complete (14 files with desktop + message metadata). Happy to defer to maintainer preference on which to merge!

@terwey
Copy link

terwey commented Jan 8, 2026

Quick chime in, if this is the wrong place for the discussion then direct me to where I should.

Does this also allow an Agent that calls a sub-agent via Task to pass a variant? One of the use cases I see is that the default model is not set (because I often switch between Providers) but you have e.g. Opus or Sonnet do the planning with maximum thinking but the Agent that will receive the sub-tasks should not have thinking enabled at all (or lower).

Currently my only work-around is duplicating agents and setting a fixed model there.

@CasualDeveloper
Copy link
Contributor

CasualDeveloper commented Jan 11, 2026

Does this also allow an Agent that calls a sub-agent via Task to pass a variant? One of the use cases I see is that the default model is not set (because I often switch between Providers) but you have e.g. Opus or Sonnet do the planning with maximum thinking but the Agent that will receive the sub-tasks should not have thinking enabled at all (or lower).

@terwey Quick check - if your goal is simply "Main agent currently running in High mode, worker always runs in Low mode", this PR (and mine #7156) already handles that via a frontmatter config. You can just set variant: "low" in your worker agent's file, and it will respect that setting even if the Main agent is running in "High" mode (subagents don't inherit parent variant).

However, since you mentioned "duplicating agents" as a workaround, I assume you need the flexibility to call the same worker agent sometimes with Low variant and sometimes with High (or changing models dynamically).

For that dynamic workflow, we could add new model and variant parameters to the Task tool schema so you can explicitly override it at call time:
Thinking: "Calling worker agent with Claude 4.5 Haiku set to low variant..."
Call: /task agent=worker model=anthropic/claude-haiku-4-5 variant=low

The Trade-offs:
I feel like an agent is often a "bundle" of {System Prompt + Model}. I'd love your (and @rekram1-node's) thoughts on the risks of breaking that bundle dynamically? I had Gemini generate some examples:

  1. Prompt Incompatibility:
    • Scenario: An oracle agent uses Claude 4.5 and its system prompt relies on <userStyle/> tags or other specific XML structures.
    • Failure: A user overrides with model="glm-4.7-free" because their Claude plan is rate-limited. The model treats the XML tags as literal text, possibly degrading output quality without throwing an error. The "Oracle" may become a confused GLM-4.7 instance.
  2. Context Window Overflows:
    • Scenario: A bug-investigator agent is designed for gemini-3-pro (2M context) and is passed a large log file.
    • Failure: Caller overrides to gpt-5.2 (128k context) thinking that it's a smarter model. The task fails immediately or, worse, silently truncates context, leading to incorrect answers.
  3. Cost/Safety Bypass:
    • Scenario: A shell-caller agent is configured to use claude-4-5-haiku to keep costs low for background tasks.
    • Failure: The user (or the main LLM agent) accidentally overrides it to o3-pro. The user would then lose architectural control over cost and latency.

Is explicit override what we want here (accepting those risks)? Or do we feel like they're overblown? If so, I'm happy to add the override parameters to my PR #7156 if we agree the flexibility outweighs the risk!

@terwey
Copy link

terwey commented Jan 11, 2026

The exact point is that I don't want to have multiple agents preconfigured tied to a provider or thinking. This might be great for defaults, but right now I need to restart opencode when I run out of tokens for a specific model.

I understand the concerns about a model maybe not fitting the context as expected; however this should be the concern to the operator. The Planner agent at the start could be informed that for some tasks use High, Med, Low and specific providers and then when approved you hit go and the agents can correctly assign tasks to the agents+provider+variant as needed. Provided no info? Just use the defaults, if you set no model in an agent it just takes whatever you had set when using an orchestrator or builder.

I right now "solved" (but still tied to a provider) by having an agent implementer without a model and implementer-fast fixed to sonnet-no-thinking. I use Opus to do the planning and in the Prompt it knows that for thinking tasks to use implementer and the rest use -fast. This works, however I sometimes want a middle ground and not burn my tokens for Opus because something was slightly more complicated than fast could handle.

Maybe my use case is very specific, I don't know, but I can spend quite some time planning with the models before I let them do work and they do respect the wishes which agents to use properly.

@CasualDeveloper
Copy link
Contributor

CasualDeveloper commented Jan 12, 2026

The exact point is that I don't want to have multiple agents preconfigured tied to a provider or thinking. This might be great for defaults, but right now I need to restart opencode when I run out of tokens for a specific model. ...

@terwey Thanks for the feedback - I do like being able to dynamically control the agent/variant used without restarting, so I've had my agent implement it in my PR #7156. The main agent can now call Task tool with optional model and variant parameters:

{
  description: Refactor auth module,
  prompt: Refactor the auth module to use OAuth2,
  subagent_type: implementer,
  model: anthropic/claude-sonnet-4-20250514,
  variant: low
}

Priority order: param override → agent config → model from parent + no variant

If you omit/don't specify both overrides, existing behaviour is preserved (agent's configured defaults apply). This lets you keep a single implementer agent and vary the model/variant at call time based on task complexity.

@rekram1-node This is an added enhancement to resolve issue #7138 — happy to discuss if you'd prefer a different direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Support default variant configuration per agent (#7156)

4 participants